#ifdef __GNUG__
#pragma implementation
#endif

#include "SunWindowSystem.h"

#include "Class.h"
#include "String.h"
#include "Error.h"
#include "File.h"
#include "OrdColl.h"
#include "ClipBoard.h"
#include "FileData.h"
#include "StreamBuf.h"
#include "System.h"
#include "../LOOKS/MotifLook.h"
#include "../LOOKS/EtLook.h"

#include "SunWindowPort.h"
#include "SunFont.h"
#include "SunBitmap.h"
#include "SunIAC.h"
#include "WindowBorder.h"

#include "pix.h"

#define CURSORS "cursors"
#define VFontExt "vf"
#define MAXSLOTS 32
#define SCRAPNAME "/tmp/winselection"

static char gBackspace, gRubout;

static struct cache {
	int type, k1, k2, k3;
	struct pixrect *pr;
} Cache[MAXSLOTS];

//---- SunRemoteData -----------------------------------------------------------

class SunRemoteData : public FileData {
public:
	SunRemoteData::SunRemoteData(char *name) : FileData(name)
		{ }
	StreamBuf *GetStreamBuf(bool forwriting);
};

StreamBuf *SunRemoteData::GetStreamBuf(bool forwriting)
{
	StreamBuf *sb= FileData::GetStreamBuf(forwriting);
	if (!forwriting) {
		int c;
		do {        // skip
			c= sb->sgetc();
			sb->stossc();
		} while (c != EOF && c != '\n');
	}
	return sb;
}

//---- DragDropService ---------------------------------------------------------

class DragDropService : public Service {
public:
	DragDropService() : Service("dragdrop")
		{ }
	int FindWindowOwner(int windowid);
	int DragDrop(int to, int code, int id, const Point &globalpos, Rectangle *droprect);
	int Dispatch(int from, const char *req, IStream &is, OStream &os);
};

//---- DragDropService ---------------------------------------------------------

static DragDropService *gDragDropService;

int DragDropService::DragDrop(int to, int code, int id, const Point &globalpos,
															Rectangle *droprect)
{
	int rc= 0;

	(*os) << code SP << id SP << globalpos;
	Talk(to, "dragdrop");
	(*is) >> rc >> *droprect;
	return rc;
}

int DragDropService::FindWindowOwner(int windowid)
{
	(*os) << windowid;
	Talk(2, "findwindow");    // poll
	(*is) >> windowid;
	return windowid;
}

int DragDropService::Dispatch(int, const char *req, IStream &is, OStream &os)
{
	Point globalpos;
	Rectangle r;
	int code= 0, winid= 0, rc= 0;

	if (strcmp(req, "findwindow") == 0) {
		is >> winid;
		rc= gWindowSystem->FindWindowWithId(winid) ? 1 : 0;
		os << rc;

	} else if (strcmp(req, "dragdrop") == 0) {
		is >> code >> winid >> globalpos;
		WindowPort *wp= gWindowSystem->FindWindowWithId(winid); // is local ?
		if (wp)
			rc= wp->DragDropNotify(code, globalpos, &r)+1;
		os << rc SP << r;
		
	} else {
		os << "no such function";
		rc= -1;
	}
	return rc;
}

//---- UrgHandler --------------------------------------------------------------

static int sigurgcnt;

class UrgHandler : public SignalHandler {
public:
	UrgHandler() : SignalHandler(eSigUrgent, FALSE)
		{ }
	bool Notify();
};

bool UrgHandler::Notify()
{
	sigurgcnt++;
	if (sigurgcnt > 2)
		Fatal("Notify", "terminated by user");
	return TRUE;
}

//---- DamageHandler -----------------------------------------------------------

class DamageHandler : public SignalHandler {
	SunWindowSystem *sws;
public:
	DamageHandler(SunWindowSystem *s) : SignalHandler(eSigWindowChanged)
		{ sws= s; }
	bool Notify();
};

bool DamageHandler::Notify()
{
	if (sws)
		return sws->Notify();
	return FALSE;
}

//---- SunWindowSystem ---------------------------------------------------------

int SunWindowSystem::rootFd= -1;
int SunWindowSystem::parentNo= -1;
SunWindowPort *SunWindowSystem::overlayport;
pixfont *SunWindowSystem::cursors;
fullscreen_t *SunWindowSystem::FS;
pixrect *SunWindowSystem::tmppr;
pixrect *SunWindowSystem::tmppr2;
pixrect *SunWindowSystem::pens[MAXPENS];


NewMetaImpl0(SunWindowSystem,WindowSystem);

SunWindowSystem::SunWindowSystem()
{
}

void SunWindowSystem::Use()
{
}

bool SunWindowSystem::Init0()
{
	const char *parentName;

	if ((parentName= gSystem->Getenv("WINDOW_PARENT")) == 0)
		return FALSE;
		
	parentNo= win_nametonumber(parentName);
	rootFd= File::Open(parentName, 0, "SunWindowSystem::InitNew");
	if (rootFd < 0) {
		fprintf(stderr, "can't open rootwindow\n");
		return FALSE;
	}

	struct pixwin *pw= pw_open(rootFd);
	gDepth= pw->pw_pixrect->pr_depth;

	if (gDepth > gMaxDepth) {
		if (gMaxDepth < 8)
			gDepth= 1;
		else
			gDepth= 8;
	}
		
	screenRect= Rectangle(0, 0, pw->pw_pixrect->pr_size.x, pw->pw_pixrect->pr_size.y);
	wcmap= new SunColorMap(gDepth);
	pw_close(pw);

	char *pathname= form("./%s.%s", CURSORS, VFontExt);
	if ((cursors= pf_open(pathname)) == NULL) {
		sprintf(pathname, "%s/sunfonts/%s.%s", gEtDir, CURSORS, VFontExt);
		if ((cursors= pf_open(pathname)) == NULL) {
			fprintf(stderr, "can't open cursorfont %s\n", pathname);
			fprintf(stderr, "(please install the vfonts in ET_DIR/sunfonts)\n");
			return FALSE;
		}
	}

	if (_sunwindow_ttychars(gBackspace, gRubout)) {
		gBackspace= '\b';
		gRubout= 0x7f;
	}

	gSystem->AddSignalHandler(new DamageHandler(this));
	gSystem->AddSignalHandler(new UrgHandler);

	gDragDropService= new DragDropService;
	
	return TRUE;
}
		
SunWindowSystem::~SunWindowSystem()
{
	register int i;

	if (tmppr)
		pr_destroy(tmppr);
	if (tmppr2)
		pr_destroy(tmppr2);
	if (cursors)
		pf_close(cursors);
	for (i= 0; i < MAXSLOTS; i++)
		if (Cache[i].pr)
			pr_destroy(Cache[i].pr);
	for (i= 0; i < MAXPENS; i++)
		if (pens[i])
			pr_destroy(pens[i]);
	if (FS)
		fullscreen_destroy(FS);

	SafeDelete(gDragDropService);
	if (rootFd >= 0) {
		File::Close(rootFd, "SunWindowSystem::~SunWindowSystem");
		rootFd= -1;
	}
}

void SunWindowSystem::DevCheckSelection()
{
	/*
	u_long modtime;
	gSystem->GetPathInfo(SCRAPNAME, 0, 0, 0, &modtime);
	if (modtime > scrapModTime)
		gClipBoard->SetData(new SunRemoteData(SCRAPNAME));
	*/
}

int SunWindowSystem::DevHaveSelection(Data *data)
{
	{
		OStream os(SCRAPNAME);
		os << "TYPE=1, ITEMS=0, ITEMBYTES=1, PUBFLAGS=1, PRIVDATA=0\n";
		//buf= gClipBoard->GetExtSelection(&len, cDocTypeAscii, 0);
		char *buf= "ET++ Clipboard";
		int len= strlen(buf);
		os.write((byte*)buf, len);
	}
	gSystem->GetPathInfo(SCRAPNAME, 0, 0, 0, &scrapModTime);
	return WindowSystem::DevHaveSelection(data);
}

bool SunWindowSystem::Notify()
{
	if (overlayport == 0) {
		WindowSystem::allwins->ForEach(SunWindowPort,TestDamage)();
	}
	return TRUE;
}

WindowPort *SunWindowSystem::MakeWindow(Window *bw, WindowType wt, GrCursor curs)
{
	return new SunWindowPort(bw, wt, curs);
}

VObject *SunWindowSystem::MakeWindowBorder(WindowType wt, VObject *in,
											char *title, bool block, bool fixed)
{
	switch (wt) {
	case eWinPulldownMenu:
	case eWinPopupMenu:
		return WindowSystem::MakeWindowBorder(wt, in, title, block, fixed);
	default:
		if (gLook == gET)
			return ::MakeEtWindowBorder(wt, in, title, block, fixed);
		return ::MakeWindowBorder(wt, in, title, block, fixed);
	}
}

FontManager *SunWindowSystem::MakeFontManager()
{
	return new SunFontManager();
}

DevBitmap *SunWindowSystem::MakeDevBitmap(Point sz, u_short *data,
								u_short depth, bool freedata, u_short)
{
	return new SunBitmap(sz, data, depth, freedata);
}

//---- pixrect cache -----------------------------------------------------------

int findpr(char type, pixrect **pr, int wd, int ht, int k1, int k2, int k3)
{
	static int last= 0;
	int stop, i, found= FALSE, emptyslot= -1;

	if (last)
		stop= last-1;
	else
		stop= MAXSLOTS-1;

	for (i= last; i != stop; i= (i+1) % MAXSLOTS) {
		if (Cache[i].pr == 0) {
			emptyslot= i;
			continue;
		}
		if (Cache[i].type == type
				&& Cache[i].pr->pr_size.x == wd && Cache[i].pr->pr_size.y == ht
				&& Cache[i].k1 == k1 && Cache[i].k2 == k2 && Cache[i].k3 == k3) {
			found= TRUE;
			break;
		}
	}
	if (! found) {
		if (emptyslot >= 0)
			i= emptyslot;
		else
			pr_destroy(Cache[i].pr);
		Cache[i].pr= mem_create(wd, ht, 1);
		Cache[i].type= type;
		Cache[i].k1= k1;
		Cache[i].k2= k2;
		Cache[i].k3= k3;
	}
	*pr= Cache[last= i].pr;
	return found;
}

int SunWindowSystem::DevFindWindow(const Point &p)
{
	return win_findintersect(SunWindowSystem::rootFd, p.x, p.y);
}

bool SunWindowSystem::DevIsEtWindow(int winid)
{
	bool rc= FALSE;
	char winname[200];
	win_numbertoname(winid, winname);
	int fd= File::Open(winname, 0);
	if (fd >= 0) {
		if (win_getuserflags(fd) & 0x10000000)
			rc= TRUE;
		File::Close(fd);
	}
	return rc;
}

static int lastid, lastto;

int SunWindowSystem::DevDragDrop(int code, int id, const Point &globalpos, Rectangle *droprect)
{
	if (lastid != id) {
		lastid= id;
		lastto= gDragDropService->FindWindowOwner(id);
	}
	return gDragDropService->DragDrop(lastto, code, id, globalpos, droprect);
}

IACImpl *SunWindowSystem::MakeRpcImpl(IAC *sc)
{
	return new SunIAC(sc);
}

